/** 
 * @author Riccardo Manfrin [namesurname at gmail dot com]
 * 
 * @brief  Wireless tools hooks towards wireless extensions (IOCTLS)
 * 
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h> /* memset */
#include <sys/socket.h> /* required by wireless.h */
#include <linux/types.h> /* requred by (recent) wireless.h */
#include <linux/if.h> /* required by IFNAMSIZ define */
#ifdef CALRADIO
  #include "cal_specs/wireless.h"
#else
  #include <linux/wireless.h>
#endif
#include <sys/ioctl.h>
#include "data_sock.h"
#include "wifi.h"
#include "debug.h"

/** ENUMERATED SETUP PARAMETERS **/
enum{
	RATE,
	CHANNEL
} param;

/** STATIC IWTOOLS COMMUNICATION DATA STRUCTURE **/
static struct iwreq req;

/** STATIC SEMAPHORE FOR WIFI ENABLED INTERFACE **/
static wifi_state wifi_enabled = WIFIDISABLED;

/** ERRORS HANDLING **/
enum {
	SOCKFD_ERR,
	IOCTL_FAILED
} err_code;

static void error(int e);

/** STATIC FUNCTION TO SET THE PARAMETERS SETUP **/
static int _set(int prm, void *valptr);
void *_get(int prm);

static void error(int e)
{
	printf("wifi.c : ERR_CODE %i\n",(int)e);
	return;
}

static int _set(int prm, void *valptr){
	int i = -1;
	char *ifc = ds_get_iface();
	if(ifc!=NULL)
	{
          switch(prm)
          {
                  case(RATE):
                  {
                          /** SETUP OF THE DATA STRUCTURE FOR THE RATE **/
                          memset(&req, 0, sizeof(req));
                          //Setting the device to route the ioctl
                          strncpy(req.ifr_ifrn.ifrn_name, ds_get_iface(), IFNAMSIZ);
                          //Setting data
                          req.u.bitrate.value = (int) *((int *)valptr);
                          req.u.bitrate.fixed = 1;
                          i=ds_get_sockfd();
                          if(i) i = ioctl(i, SIOCSIWRATE, &req);
                          else {error(SOCKFD_ERR); return -1;}
                          if(i<0) {error(IOCTL_FAILED); return -1;}
                          dbg_printf(5, "wifi.c: \tRATE SET (%i)\n", (int) *((int *)valptr));
                          break;
                  }
                  case(CHANNEL):
                  {
                          /** SETUP OF THE DATA STRUCTURE FOR THE FREQ **/
                          memset(&req, 0, sizeof(req));
                          //Setting the device to route the ioctl
                          strncpy(req.ifr_ifrn.ifrn_name, ds_get_iface(), IFNAMSIZ);
                          //Setting data
                          req.u.freq.m = (int) *((int*)valptr) ;
                          req.u.freq.e = 0;
  #ifndef CALRADIO
                          req.u.freq.flags = IW_FREQ_FIXED;
  #endif
                          i=ds_get_sockfd();
                          if(i) i = ioctl(i, SIOCSIWFREQ, &req);
                          else {error(SOCKFD_ERR); return -1;}
                          if(i<0) {error(IOCTL_FAILED); return -1;}
                          dbg_printf(5, "wifi.c: \tCHANNEL SET (%i)\n", (int) *((int*)valptr));
                          break;
                  }
                  default:
                  {
                          break;
                  }
          }
          return 0;
        }
	return -1;
}

void *_get(int prm){
  int i = -1;
  char *ifc = ds_get_iface();
  if(ifc!=NULL)
  {
    switch(prm)
    {
      case(CHANNEL):
      {
	/** SETUP OF THE DATA STRUCTURE FOR THE RATE **/
	memset(&req, 0, sizeof(req));
	//Setting the device to route the ioctl
	strncpy(req.ifr_ifrn.ifrn_name, ds_get_iface(), IFNAMSIZ);
	i=ds_get_sockfd();
	if(i) i = ioctl(i, SIOCGIWFREQ, &req);
	else {error(SOCKFD_ERR); return NULL;}
	if(i<0) {error(IOCTL_FAILED); return NULL;}
	if(req.u.freq.m > 14) req.u.freq.m = ((req.u.freq.m / 100000) - 2407) / 5;
	return (void *) &req.u.freq.m;
      }
      default:
      {
	break;
      }
    }
  }
  return NULL;
}

/** OUTSIDE WORLD INTERFACE **/

void wifi_setEn(wifi_state val){
  wifi_enabled=val;
}

wifi_state wifi_getEn(void){
  return wifi_enabled;
}

int wifi_setCh(int i){
	return _set(CHANNEL, &i);
}

int wifi_getCh(void){
  int ch = *((int*)_get(CHANNEL));
  return ch; 
}

int wifi_setRate(float i){
	int val =(int) (i * 1e6);
	return _set(RATE, &val);
}

